Skip to content

Multi-cohort support with per-member initial balance#358

Merged
briansmiley merged 31 commits intomainfrom
multi-cohort
Apr 13, 2026
Merged

Multi-cohort support with per-member initial balance#358
briansmiley merged 31 commits intomainfrom
multi-cohort

Conversation

@crthpl
Copy link
Copy Markdown
Contributor

@crthpl crthpl commented Mar 5, 2026

Summary

  • Multi-cohort architecture: Routes are now per-cohort (/[cohort_name]/market, etc.), each cohort has its own SQLite database, and a global database tracks users/membership across cohorts
  • Per-member initial balance: Admins can configure an initial clip balance when adding members to a cohort. Falls back to legacy defaults (admin=100M, non-admin=0) when not set
  • Admin cohort detail page: Member management moved from inline on /admin to a dedicated /admin/cohorts/[name] page with user combobox, email batch add, and initial balance inputs
  • Python client update: metagame package updated for multi-cohort WebSocket endpoints (/api/ws/{cohort_name}) and cohort discovery via REST API
  • Default cohort config: New default_cohort_id setting for python client/scenarios server targeting

fixes: ARB-357 ARB-382 ARB 415

Test plan

  • Start dev.sh, login as admin, verify /admin shows cohort list with chevron links
  • Click a cohort to navigate to detail page, verify members load
  • Add a user with an initial balance, verify it's stored and displayed
  • Login as that user in the cohort, verify account is created with the configured balance
  • Add members by email with initial balance, verify persistence
  • NULL initial_balance preserves legacy defaults (admin=100M, non-admin=0)
  • Python client connects via cohort-specific WebSocket endpoint
  • Read-only toggle still works from both admin list and detail page

crthpl added 3 commits March 2, 2026 22:26
- Add global SQLite DB for users, cohorts, membership, and config
- Per-cohort WebSocket routes at /api/ws/:cohort_name
- REST endpoints for cohort listing and admin management
- Restructure frontend routes under /[cohort_name]/
- Cohort selection page with auto-redirect for single cohort
- Admin page for cohort/member/config management
- Public auction access with auction-only mode
- Cohort-scoped localStorage keys
- Legacy DB auto-migration support
Resolve conflicts:
- websocket_sudo.rs: drop removed market positions test
- api.svelte.ts: keep both auctionOnly and sudoEnabled
- +layout.svelte: keep stripped auth-only root layout
- market/+page.svelte: keep getCurrentCohort, drop scenariosApi
- Move performance page under [cohort_name]/
- Update scroll handler with hysteresis from main
… multi-cohort support

- Add initial_balance column to cohort_member table with migration
- Look up configured initial balance on account creation, falling back to legacy defaults (admin=100M, non-admin=0) when NULL
- Split member management into dedicated /admin/cohorts/[name] detail page
- Simplify admin page cohort list with navigation links
- Add default_cohort_id config option
- Update python client for multi-cohort WebSocket endpoints
@crthpl crthpl requested a review from a team as a code owner March 5, 2026 18:36
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
platform Ready Ready Preview, Comment Apr 13, 2026 7:52pm

Request Review

- Add create_if_missing param to DB::init_with_path
- Clean up global DB row if cohort DB init fails (prevents UNIQUE constraint on retry)
- Add existing_db checkbox to admin UI (fails if DB file doesn't exist)
- Add GlobalDB::delete_cohort for rollback
- Add apiBase.ts, CORS and cross-origin fixes
sqlite:///data/foo.sqlite should resolve to /data/, not data/
- Backend endpoint lists .sqlite files not yet used by cohorts
- Frontend shows them as buttons that fill the slug and check "use existing DB"
- CohortState.is_read_only is now AtomicBool, loaded per-message
- Admin toggle updates the atomic, no restart needed
- New migration adds email column to global_user
- ensure_global_user stores email from Kinde auth
- Cohort members query falls back to global_user email
- Admin "All Users" list shows emails and searches by email
- Display name editing for all users on accounts page (cross-cohort)
- Admin user management: edit names, toggle admin, delete users with confirm modals
- Account color coding: color field in proto/DB, color picker for admins, colored dots in act-as dropdown, colored header when acting as colored account
- Switch cohort button only shown with multiple cohorts, moved to sidebar footer
- Editable initial balance for non-instantiated cohort members
- Fix 404 on admin page reload (disable prerender/SSR for admin routes)
- Hide read-only toggle from main admin page (keep on cohort detail page)
briansmiley and others added 2 commits April 13, 2026 09:57
Trivial resolutions:
- .gitignore: combine both additions
- schema protos: use `optional string color` from main
- .sqlx caches: keep both renamed versions
- airtable_users.rs: accept multi-cohort's deletion
- schema-js: take main's generated files

Backend resolutions:
- convert.rs: take main's expanded Account formatting
- db.rs: keep multi-cohort's global_user_id checks, main's color
  validation (Option<String> with trim/validate/lowercase)
- handle_socket.rs: keep multi-cohort's admin_id owner logic,
  use Option color type
- websocket_universes test: use color: None

Frontend resolutions:
- +page.svelte: keep multi-cohort's cohort selector (replaces
  main's /auction redirect)
- +layout.svelte: keep multi-cohort's simplified root layout,
  port main's normalizeAccountColor to cohort layout
- auction page: add main's sidebar auto-collapse
- appSideBar: merge cohort URLs with main's MarketName component
  and Performance page link
- selectMarket: cohort URLs + MarketName component
- actAs: keep both accountColor and disambiguatedAccountNames
- api.svelte.ts: keep both checkAdminAccess and
  disambiguatedAccountNames

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Regenerate schema-js protobuf bindings (adds auctionOnly to IAuthenticated)
- Remove duplicate color properties in createAccount.svelte
- Fix migration timestamp conflict (rename arbor_pixie_initial_balance)
- Remove duplicate add_account_color migration
- Port normalizeAccountColor and accountHeaderStyle to cohort layout
- Remove unused mut on is_member in handle_socket.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
briansmiley and others added 2 commits April 13, 2026 10:24
- Remove needless raw string hashes in global_db.rs and db.rs
- Use format string interpolation and is_some_and per clippy
- Add backticks to doc comments referencing identifiers
- Fix prettier formatting in createAccount.svelte
- Remove unused accountColor function in actAs.svelte

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The variable is reassigned inside a #[cfg(feature = "dev-mode")] block,
so cargo check without the feature flag incorrectly reports it as unused
mut, but it's required when dev-mode is enabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update websocket_visibility tests for multi-cohort AppState
  (use global_db + cohort pattern instead of app_state.db)
- Fix color insert: use unwrap_or_default() to insert empty string
  instead of NULL for NOT NULL column

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@crthpl
Copy link
Copy Markdown
Contributor Author

crthpl commented Apr 13, 2026

No cohorts image:
image

TODO:

  • Maybe we want a reload button next to logout in case we add them after they opened this page and we scramble to our laptops to add their account.
  • Number validation on Initial Balance field in Admin Cohort page, both frontend and backend
  • Logic of default cohort.
    • currently, if you only have one cohorts, it flashes the cohort selection page then automatically picks that cohort.
      • Make it not flash
    • currently, it shows the cohort selection screen after every reload. probably want to set in localStorage.
      • Set current cohort in localStorage
  • Make scenario server work with cohorts
  • Show emails in user list for cohort admin page
  • Admins have access to all cohorts. But if an admin goes to a cohort that they weren't explicitly added to, they can do things normally, but they don't show up in the members list on the admin cohort page. What's up with that?
  • We can now make people admins from the Admin page; this sets a field in the global users table. People are considered admins if they have the Kinde admin role OR if they are set as admins the users table. The field in the global users should be automatically set if they have the Kinde admin role, and then we can just the that field.
  • Fix duplicate user names issues
    • We should munge names (e.g. -g34ba8) for global users; we don't yet. This currently also causes wierdness when adding them to cohorts, where suddenly names are munged.
  • Flattened account hierarchy?
    • No more subaccounts, only accounts
  • Reloading on Admin page should stay on admin page.
  • If people log in they don't have a name until they get added to a cohort:
image
  • Adding someone by email to a cohort should immediately add them if they already have an entry in the users table
  • Have one /admin API request that sends all data, including users.

- Regenerate .sqlx/ offline query cache for CI (SQLX_OFFLINE=true)
- Add #[allow(unused_mut)] on is_member since mut is only needed
  under dev-mode feature gate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add # Errors doc sections to get_legacy_kinde_users,
  get_balance_by_global_user_id, set_global_user_id
- Allow clippy::too_many_arguments on handle_client_message

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove autofocus from inline edit inputs (a11y_autofocus)
- Add svelte-ignore comments for modal backdrop div interactions
  (a11y_no_noninteractive_element_interactions, a11y_click_events_have_key_events)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Allow option_option on UpdateConfigRequest (intentional PATCH
  semantics: distinguishes "not provided" from "set to null")
- Use let...else instead of match for read_dir fallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…g panics doc

- Remove unused CohortInfo import in test_utils.rs
- Replace redundant closures with ToString::to_string in auth.rs
- Add # Panics doc section to spawn_test_server

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Auto-fix: inline format args across all test files and db.rs
- Auto-fix: doc backticks in test comments
- Allow similar_names, too_many_lines, used_underscore_binding in
  test files (splitting tests or renaming market_a/market_b hurts
  readability more than it helps)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crthpl added 2 commits April 13, 2026 12:37
Previously, creating a cohort without 'Use existing database' checked
would silently adopt an existing .sqlite file at the same path. Now
returns 400 with a message pointing to the checkbox.
@briansmiley briansmiley merged commit 7cd390c into main Apr 13, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants